package cz.uhk.max.matrix.impl;

import java.util.ArrayList;
import java.util.List;

import javax.swing.plaf.multi.MultiRootPaneUI;

import cz.uhk.max.matrix.Col;
import cz.uhk.max.matrix.Container;
import cz.uhk.max.matrix.Matrix;
import cz.uhk.max.matrix.IMatrixDao;
import cz.uhk.max.matrix.Row;
import cz.uhk.max.matrix.TreeMatrix;

/**
 * Class MatrixDaoImpl includes methods for reuse.
 * 
 * 
 * */
public class MatrixDaoImpl implements IMatrixDao {

	public static Col max(Col ai, Col ay) {
		if (ai.getValue() >= ay.getValue()) {
			return ai;
		} else {
			return ay;
		}
	}

	public static Col min(Col ai, Col ay) {
		if (ai.getValue() <= ay.getValue()) {
			return ai;
		} else {
			return ay;
		}
	}

	
	public boolean isVectorSolution(Matrix A, Matrix B, Matrix vector) {
		Matrix a = multiMatrixWithVector(A, vector);
		Matrix b = multiMatrixWithVector(B, vector);

		if (isSame(a, b)) {
			return true;
		} else {
			return false;
		}
	}
	
	public Long indexOfRowWithMax(TreeMatrix respA, TreeMatrix respB){
		List<Col> maximum;
		Col maxOfMaximum;
		
		maximum = maximum(respA);
		maximum.addAll(maximum(respB));
		maxOfMaximum = maximum(maximum);

		return (long) maxOfMaximum.getI();
	}
	/**
	 * Method return vector of maximum value in each row
	 * 
	 * @return Vector of Col with maximum value in each row.
	 */
	public List<Col> maximum(Matrix m) {
		List<Col> maxList = new ArrayList<Col>();

		for (int i = 0; i < (m.getRowsCount()); i++) {
			if (m.getRows().get(i).isEliminated() == false) {
				maxList.add(maximum(m.getRows().get(i).getCols()));
			}
		}
		return maxList;
	}

	/**
	 * Method return Col, that have maximum value of columns in list.
	 * 
	 * @return Object Col with maximum value from list.
	 */
	public Col maximum(List<Col> cols) {
		List<Col> minsList = new ArrayList<Col>(cols);
		Col pom;
		for (int i = 0; i < (minsList.size() - 1); i++) {
			if (minsList.get(i + 1).getValue() < minsList.get(i).getValue()) {
				pom = minsList.get(i);
				minsList.set(i, minsList.get(i + 1));
				minsList.set(i + 1, pom);
			}
		}
		return minsList.get(minsList.size() - 1);
	}

	/**
	 * Method return Col, that have maximum value of columns in list.
	 * 
	 * @return Object Col with maximum value from list.
	 */
	public Col minimum(List<Col> cols) {
		List<Col> minsList = new ArrayList<Col>(cols);
		Col pom;
		for (int i = 0; i < (minsList.size() - 1); i++) {
			if (minsList.get(i + 1).getValue() > minsList.get(i).getValue()) {
				pom = minsList.get(i);
				minsList.set(i, minsList.get(i + 1));
				minsList.set(i + 1, pom);
			}
		}
		return minsList.get(minsList.size() - 1);
	}

	/**
	 * Method return Col, that is computed as multiplication of concrete row in
	 * matrix A with vector x.
	 * 
	 * @return Object Col with result of multiplicaton.
	 */
	public Col multiRowWithVector(Matrix A, Matrix x, Integer i) {
		Matrix A1 = new Matrix(A);
		Matrix x1 = new Matrix(x);

		List<Col> listOfMinims = new ArrayList<Col>();
		for (int j = 0; j < A1.getColsCount(); j++) {
			listOfMinims.add(min(A1.getCol(i, j), x1.getCol(j, 0)));
		}
		return maximum(listOfMinims);
	}

	/**
	 * Method return Col, that is computed as multiplication row in matrix A
	 * with vector x.
	 * 
	 * @return Object Col with result of multiplicaton.
	 */
	public Col multiRowWithVector(Row a, Matrix x) {
		List<Col> listOfMinims = new ArrayList<Col>();
		for (int j = 0; j < a.getColsCount(); j++) {
			listOfMinims.add(min(a.getCols().get(j), x.getCol(j, 0)));
		}
		return maximum(listOfMinims);
	}

	/**
	 * Method return Col, that is computed as multiplication of multidimension
	 * matrix A with vector x.
	 * 
	 * @return Object Matrix with result of multiplicaton.
	 */
	public Matrix multiMatrixWithVector(Matrix A, Matrix x) {
		// max(min(ai1,x1), min(ai2,x2), min (ai3,x3)) = bi
		Matrix result = new Matrix(A.getRowsCount(), 1);
		for (int i = 0; i < A.getRowsCount(); i++) {
			result.getRows().get(i).getCols().set(0,
					multiRowWithVector(A, x, i));
		}
		return result;
	}
	
	/**
	 * Method return TreeMatrix contents values of A that is not greater 
	 * than value of x at column or value of x(j) if a(j) is greater
	 * Method set information about elimination of rows.
	 * @return Object TreeMatrix with values of A and restriction x
	 */
	public TreeMatrix respectVector(TreeMatrix A, TreeMatrix x) {
		TreeMatrix result = new TreeMatrix(A.getRowsCount(), A.getColsCount());
		for (int i = 0; i < A.getRowsCount(); i++) {
			result.getRows().get(i).setEliminated(A.getRows().get(i).isEliminated());
			for (int j = 0; j < A.getColsCount(); j++) {
				if (A.getCol(i, j).getValue() > x.getCol(j, 0).getValue()) {
					result.setCol(i, j, x.getCol(j, 0).getValue());
					
				} else {
					result.setCol(i, j, A.getCol(i, j).getValue());
				}
				;
			}
		}
		return result;
	}

	/**
	 * Method return Matrix, that is computed as addition of multidimension
	 * matrices A and B.
	 * 
	 * @return Object Matrix with result of multiplicaton.
	 */
	public Matrix additionMatrices(Matrix A, Matrix B) {
		if (A.getRowsCount() != B.getRowsCount()
				|| A.getColsCount() != B.getColsCount()) {
			return null;

		} else {
			Matrix result = new Matrix(A.getRowsCount(), B.getColsCount());
			for (int i = 0; i < A.getRowsCount(); i++) {
				for (int y = 0; y < A.getColsCount(); y++) {
					if (A.getCol(i, y).getValue() > B.getCol(i, y).getValue()) {
						result.setCol(i, y, A.getCol(i, y).getValue());
					} else {
						result.setCol(i, y, B.getCol(i, y).getValue());
					}
				}
			}
			return result;
		}
	}

	/**
	 * Method compare values of two matrices.
	 * 
	 * @return Boolean value. True if values of matrices is same.
	 */
	public Boolean isSame(Matrix x1, Matrix x2) {
		Matrix x = new Matrix(x1);
		Matrix y = new Matrix(x2);

		Boolean same = true;

		for (int i = 0; i < x.getRowsCount(); i++) {
			for (int j = 0; j < x.getColsCount(); j++) {
				if (!x1.getCol(i, j).getValue().equals(
						y.getCol(i, j).getValue())) {
					same = false;
					// if value of one column is not the same
				}
			}
		}
		return same;
	}

	/**
	 * Method return minimum value from one matrix.
	 * 
	 * @return Return Col with minimum value.
	 */
	public Col minimum(Matrix m) {
		Col min = m.getCol(0, 0);
		for (int i = 0; i < m.getRowsCount(); i++) {
			for (int j = 0; j < m.getColsCount(); j++) {
				if (m.getCol(i, j).getValue() < min.getValue()) {
					min = m.getCol(i, j);
				}
			}
		}
		return min;
	}

	/**
	 * Method return minimum value from one row.
	 * 
	 * @return Return Col with minimum value.
	 */
	public Col minimum(Row r) {
		Col min = r.getCols().get(0);
		for (int j = 0; j < r.getColsCount(); j++) {
			if (r.getCols().get(j).getValue() < min.getValue()) {
				min = r.getCols().get(j);
			}
		}
		return min;
	}

	public void changeSide(int side, Matrix A, Matrix B) {
		Row temp = new Row(A.getRows().get(side));
		A.getRows().set(side, B.getRows().get(side));
		B.getRows().set(side, temp);
	}

	public Container nonEqualsRows(Matrix A, Matrix B, Matrix bound) {
		Matrix aTemp = multiMatrixWithVector(A, bound);
		Matrix bTemp = multiMatrixWithVector(B, bound);
		List aRows = new ArrayList<Row>();
		List bRows = new ArrayList<Row>();

		Container c = new Container();

		for (int i = 0; i < A.getRowsCount(); i++) {
			if (aTemp.getCol(i, 0).getValue() != bTemp.getCol(i, 0).getValue()) {
				aRows.add(A.getRows().get(i));
				bRows.add(B.getRows().get(i));
			}
		}

		if (aRows.size() != 0) {
			System.out.println("aRows size:" + aRows.size());
			c.setA(new Matrix(aRows));
			c.setB(new Matrix(bRows));
			c.setCount(aRows.size());
			return c;
		} else {
			return null;
		}
	}
}
